#ifndef TOPIC_RECEIVER_H
#define TOPIC_RECEIVER_H

#include <stdint.h>
#include <stdlib.h>

#include <vector>

#include <ros/publisher.h>

#include <topic_tools/shape_shifter.h>

#include "bsm_msgs/BSM_multi.h"

#include "bsm_msgs/CompressedMsg.h"
#include <boost/thread.hpp>

#if WITH_OPENFEC
extern "C"
{
#include <of_openfec_api.h>
}
#endif

#include "topic_core/udp_packet.h"

namespace topic_recver
{

	struct Message
	{
		Message(uint16_t id)
			: id(id), size(0), complete(false)
		{
		}

		Message()
		{
		}

		~Message()
		{
		}

		uint32_t getLength() const
		{
			return size;
		}

		uint8_t *getData()
		{
			return payload.data();
		}

		bool decompress(Message *dest);

		uint16_t id;
		topic_core::UDPFirstPacket::Header header;
		std::vector<uint8_t> payload;
		size_t size;
		std::vector<bool> msgs;

		bool complete;

#if WITH_OPENFEC
		boost::shared_ptr<of_session_t> decoder;
		boost::shared_ptr<of_parameters_t> params;
		unsigned int received_symbols;
		std::vector<boost::shared_ptr<std::vector<uint8_t>>> fecPackets;
#endif
	};

	struct TopicReceiver
	{
		TopicReceiver();
		~TopicReceiver();

		ros::Publisher publisher;

		uint32_t md5[4];
		std::string md5_str;
		std::string msg_def;

		bool compressed;

		int last_message_counter;

		bool waiting_for_subscriber;

		void takeForDecompression(const boost::shared_ptr<Message> &compressed);

		void publish(const boost::shared_ptr<topic_tools::ShapeShifter> &msg);
		void publishCompressed(const bsm_msgs::CompressedMsgConstPtr &msg);
		void handleSubscriber();

	private:
		boost::shared_ptr<Message> m_compressedMsg;
		boost::condition_variable m_cond;
		boost::mutex m_mutex;

		boost::thread m_decompressionThread;
		bool m_decompressionThreadRunning;
		bool m_decompressionThreadShouldExit;

		boost::shared_ptr<topic_tools::ShapeShifter> m_msgInQueue;
		bsm_msgs::CompressedMsgConstPtr m_compressedMsgInQueue;
		ros::WallTime m_queueTime;

		void decompress();
	};

} // namespace topic_recver

#endif
